#pragma once

#include "Common.hpp"
#include "Buffer.hpp"
#include "Channel.hpp"
#include "Loop.hpp"
#include "Socket.hpp"
#include "Any.hpp"

class Connection;
// DISCONECTED -- 连接关闭状态；   CONNECTING -- 连接建立成功-待处理状态
// CONNECTED -- 连接建立完成，各种设置已完成，可以通信的状态；  DISCONNECTING -- 待关闭状态
typedef enum { DISCONNECTED = 0, CONNECTING, CONNECTED, DISCONNECTING}ConnStatu;
using PtrConnection = std::shared_ptr<Connection>;
class Connection: public std::enable_shared_from_this<Connection>{
private:  
    // 这四个回调函数, 是让服务器模块来设置的（其实服务器模块的处理回调也是组件使用者设置的）
    // 换句话说, 这几个回调都是组件使用者使用的
    using ConnectedCallback = std::function<void(const PtrConnection&)>;
    using MessageCallback = std::function<void(const PtrConnection&, Buffer*)>;
    using ClosedCallback = std::function<void(const PtrConnection&)>;
    using AnyEventCallback = std::function<void(const PtrConnection&)>;

    /* 五个 Channel 的事件回调函数 */
    // 描述符可读事件后调用的函数, 接收 socket 数据放到接收缓冲区中, 然后调用 _message_callback
    void HandleRead(){
        char buf[65536];
        int ret = _socket.NonBlockRecv(buf, sizeof(buf) - 1); // 获取非阻塞下的实际接收长度
        if(ret < 0){
            return ShutdownInLoop();
        }
        // 这里的等于 0 表示的是没有读取到数据, 而不是连接断开, 连接断开返回的是 -1
        _in_buffer.WriteAndPush(buf, ret); // 将数据放入输入缓冲区
        if(_in_buffer.ReadableSize() > 0){
            // 通常用于在已有的 std::shared_ptr 管理的对象中创建新的 std::shared_ptr，而不会增加引用计数的错误
            // 这个函数确保了在对象已经被 std::shared_ptr 管理的情况下，可以通过该对象获取到管理它的 std::shared_ptr
            return _message_callback(shared_from_this(), &_in_buffer); // 调用消息回调函数
        }
    }

    // 描述符可写事件触发后调用的函数, 将发送缓冲区中的数据进行发送
    void HandleWrite(){
        int ret = _socket.NonBlockSend(_out_buffer.GetReadPos(), _out_buffer.ReadableSize()); // 发送数据
        if(ret < 0){
            // 先判断缓冲区是否有数据可读
            if(_in_buffer.ReadableSize() > 0){
                return _message_callback(shared_from_this(), &_in_buffer); // 调用消息回调函数
            }
            return Release(); // 这个时候就是实际的关闭连接操作
        }
        _out_buffer.MoveReadOffset(ret); // 移动读指针
        if(_out_buffer.ReadableSize() == 0){
            _channel.DisableWrite(); // 发送缓冲区已清空, 禁止写事件监控
            // 如果当前是连接关闭状态, 如果有数据则发送完数据再释放连接
            if(_status == DISCONNECTING){
                return Release();
            }
        }
        return;
    }
    // 描述符触发挂断事件
    void HandleClose(){
        /*一旦连接挂断了，套接字就什么都干不了了，因此有数据待处理就处理一下，完毕关闭连接*/
        if(_in_buffer.ReadableSize() > 0){
            _message_callback(shared_from_this(), &_in_buffer); // 调用消息回调函数
        }
        return Release(); // 关闭连接

    }
    // 描述符触发错误事件
    void HandleError(){
        return HandleClose();
    } 
    // 描述符触发任意事件: 1. 刷新连接的活跃度--延迟定时销毁任务；  2. 调用组件使用者的任意事件回调
    void HandleEvent(){
        if(_enable_inactive_release == true) _loop->RefreshTimer(_conn_id);
        if(_event_callback) _event_callback(shared_from_this()); // 调用任意事件回调函数)
    }

// 连接获取之后, 所处状态下进行的各种设置（给 Channel 设置事件回调, 启动读监控）
    void EstablishInLoop(){
        // 1. 修改连接状态  2. 启动读事件监控 3. 调用回调函数
        assert(_status == CONNECTING); // 当前的状态必须一定是上层的半连接状态(只有这个函数完成, 才真正处于 CONNECTED 状态)
        _status = CONNECTED; //当前函数执行完毕，则连接进入已完成连接状态

        // 原因: 一旦启动读事件监控就有可能触发读事件, 如果这个时候启动了非活跃连接销毁
        _channel.EnableRead(); // 启动读事件监控
        if(_connected_callback) _connected_callback(shared_from_this()); // 调用连接建立成功的回调函数
    }
    // 这个接口才是实际的释放接口
    void ReleaseInLoop(){
        // 1. 修改连接状态
        _status = DISCONNECTED;
        // 2. 移除连接的 Channel 事件监控
        _channel.Remove();
        // 3. 关闭读写事件监控 
        _socket.Close();
        // 4. 如果当前定时器队列中还有定时销毁任务, 则取消任务
        if(_loop->HasTimer(_conn_id)) _loop->CancelTimer(_conn_id);
        // 5. 调用关闭回调函数
        if(_closed_callback) _closed_callback(shared_from_this());
        if(_server_closed_callback) _server_closed_callback(shared_from_this()); 
    }

    // 这个接口并不是实际的发送接口，而只是把数据放到了发送缓冲区，启动了可写事件监控
    void SendInLoop(Buffer &buf){
        if(_status == DISCONNECTED) return;
        _out_buffer.WriteBufferAndPush(buf);
        // 启动写事件监控
        if(!_channel.WriteAble()) _channel.EnableWrite(); 
    }
    // 这个关闭操作并非实际的连接释放操作，需要判断还有没有数据待处理，待发送
    void ShutdownInLoop(){
        _status = DISCONNECTING; // 修改状态为待关闭状态
        if(_in_buffer.ReadableSize() > 0){
            if(_message_callback) _message_callback(shared_from_this(), &_in_buffer);
        }
        // 获取是写入数据时出错关闭， 没有待发送数据直接关闭
        if(_out_buffer.ReadableSize() > 0){
            if(!_channel.WriteAble()){
                _channel.EnableWrite(); // 启动写事件监控
            }
        }
        if(_out_buffer.ReadableSize() == 0) Release();
    }

    // 启动非活跃连接超时释放规则(sec: 延迟时间)
    void EnableInactiveReleaseInLoop(int sec){
        // 1. 设置判断标志 
        _enable_inactive_release = true;
        // 2. 如果当前定时销毁任务已存在, 则刷新延迟即可
        if(_loop->HasTimer(_conn_id)) return _loop->RefreshTimer(_conn_id);
        // 3. 不存在则新增定时销毁任务
        _loop->AddTimer(_conn_id, sec, std::bind(&Connection::Release, this));
    }
    
    // 取消非活跃任务的超时销毁
    void CancelInactiveReleaseInLoop(){
        _enable_inactive_release = false;
        if(_loop->HasTimer(_conn_id)) _loop->CancelTimer(_conn_id);
    }

    // 切换协议, 重置上下文以及阶段性处理函数
    void UpgradeInLoop(const Any &context, 
        const ConnectedCallback &conn, 
        const MessageCallback &msg, 
        const ClosedCallback &closed, 
        const AnyEventCallback &event)
    {
        
        _context = context;
        _connected_callback = conn;
        _message_callback = msg;
        _closed_callback = closed;
        _event_callback = event;
    }

public:
    Connection(EventLoop *loop, uint64_t conn_id, int sockfd):
        _conn_id(conn_id),
        _sockfd(sockfd),
        _enable_inactive_release(false),
        _loop(loop),
        _status(CONNECTING),
        _socket(sockfd),
        _channel(loop, sockfd)
    {
        _channel.SetCloseCallback(std::bind(&Connection::HandleClose, this));
        _channel.SetEventCallback(std::bind(&Connection::HandleEvent, this));
        _channel.SetReadCallback(std::bind(&Connection::HandleRead, this));
        _channel.SetWriteCallback(std::bind(&Connection::HandleWrite, this));
        _channel.SetErrorCallback(std::bind(&Connection::HandleError, this));
    }

    ~Connection(){LOG_ERROR("RELEASE CONNECTION: %p", this);}

    int Fd() const {return _sockfd;}
    int Id() const {return _conn_id;}
    bool Connected(){return _status == CONNECTED;} // 判断状态是否处于连接状态

    // 设置上下文 -- 连接建立完成时调用
    void SetContext(const Any &context){_context = context;}
    Any *GetContext() { return &_context; } // 获取的上下文(指针), 保证每次访问是同一个上下文
    
    void SetConnectedCallback(const ConnectedCallback& cb){_connected_callback = cb;}
    void SetMessageCallback(const MessageCallback& cb){_message_callback = cb;}
    void SetClosedCallback(const ConnectedCallback& cb){_closed_callback = cb;}
    void SetAnyEventCallback(const ConnectedCallback& cb){_event_callback = cb;}
    void SetSrvClosedCallback(const ClosedCallback& cb){_server_closed_callback = cb;}

    // 连接建立就绪后，进行channel回调设置，启动读监控，调用_connected_callback
    void Established(){_loop->RunInLoop(std::bind(&Connection::EstablishInLoop, this));}
    // 发送数据, 将数据放到发送缓冲区, 启动写事件监控
    void Send(const char *data, size_t len){
        // 注意: 外界传入的 data, 可能是个临时空间, 不能直接使用, 需要进行一次拷贝(构造一个临时对象, 就不需要担心被释放的问题了)
        // 因为当前只是把发送操作压入了任务池, 有可能没有被立即执行
        // 因此可能执行的时候, data 指向的空间已经被释放了
        // _loop->RunInLoop(std::bind(&Connection::SendInLoop, this, data, len)); // 错误的写法
        Buffer buf;
        buf.WriteAndPush(data, len);
        _loop->RunInLoop(std::bind(&Connection::SendInLoop, this, std::move(buf)));
    }
    // 提供给组件使用者的关闭接口 -- 并不实际关闭, 需要判断是否有数据待处理
    void Shutdown(){_loop->RunInLoop(std::bind(&Connection::ShutdownInLoop, this));}
    // 释放连接, 关闭连接, 并从管理器中移除连接信息, 启动非活跃销毁, 并定义多长时间就是非活跃, 添加定时任务
    void Release(){_loop->RunInLoop(std::bind(&Connection::ReleaseInLoop, this));}

    // 启动非活跃销毁, 并定义多长时间就是非活跃, 添加定时任务
    void EnableInactiveRelease(int sec){_loop->RunInLoop(std::bind(&Connection::EnableInactiveReleaseInLoop, this, sec));}
    // 取消非活跃销毁
    void CancelInactiveRelease(int sec){_loop->RunInLoop(std::bind(&Connection::CancelInactiveReleaseInLoop, this));}

    // 切换协议, 重置上下文以及阶段性处理函数
    void Upgrade(const Any &context, const ConnectedCallback &conn, const MessageCallback &msg, const ClosedCallback &closed, const AnyEventCallback &event)
    {
        _loop->AssertInLoop();
        _loop->RunInLoop(std::bind(&Connection::UpgradeInLoop, this, context, conn, msg, closed, event));
    } 

private:
    uint64_t _conn_id;  // 连接 ID, 便于连接的管理和查找
    // uint64_t _timer_id; // 定时器ID
    int _sockfd;        // 连接关联的文件描述符
    bool _enable_inactive_release;  // 连接是否启动非活跃销毁的判断标志，默认为false
    EventLoop *_loop;   // 连接所关联的一个EventLoop
    ConnStatu _status;  // 连接状态
    Socket _socket;     // 连接的 Socket 对象
    Channel _channel;   // 连接的 Channel 对象
    Buffer _in_buffer;  // 输入缓冲区 -- 存放从 socket 中读取到的数据
    Buffer _out_buffer; // 输出缓冲区 -- 存放要发送对端的数据
    Any _context;       // 请求的接收处理上下文对象

    // 组件内的连接关闭回调 --- 组件内设置的
    ConnectedCallback _connected_callback; // 连接建立成功的回调函数
    MessageCallback _message_callback;     // 连接接收数据的回调函数
    ClosedCallback _closed_callback;    
    AnyEventCallback _event_callback;
    
    /*组件内的连接关闭回调--组件内设置的，因为服务器组件内会把所有的连接管理起来，一旦某个连接要关闭*/
    /*就应该从管理的地方移除掉自己的信息*/
    ClosedCallback _server_closed_callback;
};
